/**
 *	The NeuroCoSA Toolkit
 *	Copyright (C) 2003-6 Stuart Meikle.
 *
 *	This is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU Lesser General Public
 *	License as published by the Free Software Foundation; either
 *	version 2.1 of the License, or (at your option) any later version.
 *
 *	This library is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *	Lesser General Public License for more details.
 *
 * @author	Stuart Meikle
 * @version	2006-halloween(mk2)
 * @license	LGPL
 */
package org.stumeikle.NeuroCoSA;
import java.util.*;
import  org.stumeikle.NeuroCoSA.NIS.*;


/** Bi-Mode Layer class. 
 *  As previously. Bimode layer is a layer which contains bimodeneurons - that is, 
 *  neurons which have learn and drive states. 
 */
public class BiModeLayer extends Layer
{
    //here lies the info service. local this time
    //synchronized publications ...
    InfoWithSingleStore		iWinningDriveNeuron;
    InfoDoubleWithSingleStore	iWinningDriveSignalStrength;
    InfoWithSingleStore 	iWinningLearnNeuron;
    InfoDoubleWithSingleStore 	iWinningLearnSignalStrength;
    InfoWithMultiStore		iAllLearningNeurons;
				//20071028 store firing strength + neuron pointer for each neuron firing above
				//noise threshold. limit 
    //working vars
    BiModeNeuron		iTmpWinningDriveNeuron;
    BiModeNeuron		iTmpWinningLearnNeuron;
    double			iTmpWinningDriveStrength;
    double			iTmpWinningLearnStrength;
// Added to simple layer    Synapse			iTmpDriveController;
    LinkedList<Neuron>		iLearningNeurons;

    public BiModeLayer(Cortex l)
    {
	super(l);

	//add the new bi-mode information that we need to , to the information service
	//this will now include 'yesterdays news' items;
	iWinningDriveNeuron = new InfoWithSingleStore( "WinningDrive", this );
	iWinningLearnNeuron = new InfoWithSingleStore( "WinningLearn", this );
	iWinningDriveSignalStrength = new InfoDoubleWithSingleStore ( "WinningDriveSigStr", 0.0, this );
	iWinningLearnSignalStrength = new InfoDoubleWithSingleStore ( "WinningLearnSigStr", 0.0, this );
	iAllLearningNeurons = new InfoWithMultiStore("AllLearningNeurons", this);

	getInfoService().addPublication( iWinningDriveNeuron );
	getInfoService().addPublication( iWinningLearnNeuron );
	getInfoService().addPublication( iWinningDriveSignalStrength );
	getInfoService().addPublication( iWinningLearnSignalStrength );
	getInfoService().addPublication( iAllLearningNeurons );

    }

    public void			getReady(){}

    protected void		resetVars()
    {
	//reset the temporary vars
	iTmpWinningLearnStrength=0.0;
	iTmpWinningDriveStrength=0.0;
	iTmpWinningLearnNeuron= null;
	iTmpWinningDriveNeuron= null;
	iLearningNeurons = new LinkedList<Neuron>(); //create a new structure to store new data 20071103
    }

    protected void 		updateVars(BiModeNeuron bm)
    {
	//update the vars depending on the firing strength of n and its mode
	boolean		state_active = false;

	//20060521 We must update even the inhibited neurons else we'll miss the max learn signal
	//if there is a drive inhibition active. this means we can't learn when success occurs.
	if ( bm.getState() == Neuron.state_excited || 
	     bm.getState() == Neuron.state_inhibited)
	    state_active = true;

	if (state_active && bm.getLearnSigStr() > iTmpWinningLearnStrength)
	{
	    iTmpWinningLearnStrength = bm.getLearnSigStr();
	    iTmpWinningLearnNeuron   = bm;
	} 
	if (state_active && bm.getDriveSigStr() > iTmpWinningDriveStrength)
	{
	    iTmpWinningDriveStrength = bm.getDriveSigStr();
	    iTmpWinningDriveNeuron = bm;
	}
	if (state_active && bm.getLearnSigStr() > bm.getExcitationThreshold())
	{
	    //remember the neurons activity 20071103
	    iLearningNeurons.add( bm );
	}
    }

    protected double		getTmpWinningDriveStrength()
    { return iTmpWinningDriveStrength; } 

    protected void		transferVarsToLIS()
    {
	//update the info service based on the tmp vars
	iWinningDriveNeuron.setValue( iTmpWinningDriveNeuron );
	iWinningLearnNeuron.setValue( iTmpWinningLearnNeuron );
	iWinningDriveSignalStrength.setDoubleValue(iTmpWinningDriveStrength);
	iWinningLearnSignalStrength.setDoubleValue(iTmpWinningLearnStrength);

	//update the super synched data too 20071103
	iAllLearningNeurons.setValue( iLearningNeurons );
    }

    public void			updateNeurons()/// I expect this to be overloaded in bimodelayer later
    {
	resetVars();
System.out.println("Bimodelayer:Updating neurons");
	ListIterator		i = getNeurons().listIterator(0);
	for(;i.hasNext();)
	{
	    BiModeNeuron	bmn = (BiModeNeuron)i.next();
	    bmn.update();
	
	    updateVars(bmn);
	}

	transferVarsToLIS();
System.out.println("Bimodelayer:Updated neurons");
    }

    public	void			updateNeurons(Vesicle lv)
    {
    	resetVars();
	ListIterator		i = getNeurons().listIterator();
	for(;i.hasNext();)
	{
	    BiModeNeuron	bmn = (BiModeNeuron)i.next();
	    bmn.update(lv);
	
	    updateVars(bmn);
	}
	transferVarsToLIS();
    }

    
/* other bits to add later
    public	void			updateNeuronsWithLateralInhibit(double signal, Vesicle lv)
    {
    	//update the neurons in the lobe. Find the strongest drive signal and
	//pass this in as a lateral inhibition to the other neurons, thus leaving
	//only one driver
	
	//(1) pass one, create all the vesicle packages.
	//(1.1) extract the max drive signal
	ListIterator		i = iInstances.listIterator();
	double			max_sig = 0.0;
	Neuron			max_d= null;
	
	for(;i.hasNext();)
	{
	    VesiclePackage	vp;
	    Vesicle		v;
	    Neuron		sn = (Neuron)i.next();
	    double		sig;
	    
	    sn.update_step1(signal,lv);
	    vp = sn.getVesiclePackage();
	    if (vp!=null)
	    {
	    	//check if we have a drive
		//check if the signal strength > max_sig
		if (vp.findType(Vesicle.DRIVE_VESICLE)!=null ||
		    vp.findType(Vesicle.PTR_DRIVE_VESICLE)!=null )
		{
		    sig = vp.getSignal();
		    if (sig > max_sig)
		    {
		    	max_sig= sig;
			max_d  = sn;
		    }
		}
	    }
	}
	
	//(1.5) create a new vesicle and signal to pass back to the neuron
	//(nothing to do here)
	//System.out.println("BiModeLobe: max sig = " + max_sig );
	//System.out.println("BiModeLobe: max d   = " + max_d );
	
	//(2) pass two, update the vesicle packages and continue with the neuron update 
	iLis.startOfLobeUpdate();
	i = iInstances.listIterator();
	for(;i.hasNext();)
	{
	    Neuron		sn = (Neuron)i.next();
	    
	    //update all but the strongest...
	    //System.out.println("BiModeLobe: loop compare sn = " + sn );
	    if (sn != max_d )
	    {
	    	//System.out.println("BiModeLobe: injecting inhibit..." );
	    	sn.updateVesiclePackage( -max_sig, iVessyD  );
	    }
	    sn.update_step2();
	    iLis.instanceUpdate(sn);
	}
	iLis.endOfLobeUpdate();
    }
*/
}

